#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip_icmp.h>
#include <netinet/in.h>				/*For protocol name enum*/
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <linux/if_ether.h>
#include <net/ethernet.h>
#include <errno.h>

/*
 * This code is taken from binaryTides.com/packet-sniffer-code-in-c-using-linux-sockets-bsd-part2/
 * And modified for my needs
 *
 * */


void ProcessPacketFrame(unsigned char *Buffer, int size);
void ProcessIPPacket(struct ethhdr *,  unsigned char* , int);
void print_ip_header(struct ethhdr *, unsigned char* , int);
void print_tcp_packet(struct ethhdr *,   unsigned char* , int);
void print_udp_packet(struct ethhdr *,  unsigned char * , int);
void print_icmp_packet(struct ethhdr *, unsigned char* , int);
void PrintData (unsigned char* , int);

int sock_raw;
FILE *logfile;
int tcp = 0, udp = 0, icmp = 0, others = 0, igmp = 0, total = 0, i, j;
struct sockaddr_in source,dest;

int main(int argc, char *argv[])
{
	int saddr_size , data_size;
	struct sockaddr saddr;

	unsigned char *buffer = (unsigned char *)malloc(65536); //Its Big!

	logfile=fopen("log.txt","w");
	if(logfile==NULL) 
	{	
		printf("Unable to create file.");
	}
	printf("Starting...\n");

	//Create a raw socket that shall sniff
	sock_raw = socket(AF_PACKET , SOCK_RAW , htons(ETH_P_ALL));
	if(sock_raw < 0)
	{
		printf("socket() failed with error %d, ('%s')\n", errno, strerror(errno));
		return 1;
	}
	setsockopt(sock_raw, SOL_SOCKET, SO_BINDTODEVICE, "eth0", strlen("eth0") + 1);
	while(1)
	{
		saddr_size = sizeof saddr;
		//Receive a packet
		data_size = recvfrom(sock_raw , buffer , 65536 , 0 , &saddr , (socklen_t *)&saddr_size);
		if(data_size <0 )
		{
			printf("Recvfrom error , failed to get packets\n");
			return 1;
		}
		//Now process the packet frame
		ProcessPacketFrame(buffer , data_size);
	}
	close(sock_raw);
	printf("Finished");
	return 0;
}

void ProcessPacketFrame(unsigned char *Buffer, int size)
{	
	struct ethhdr *ethdr = (struct ethhdr *)Buffer;

	switch(ntohs(ethdr->h_proto))
	{
		case ETH_P_IP:
				ProcessIPPacket(ethdr, Buffer+sizeof(struct ethhdr), size - sizeof(struct ethhdr));
				break;
		default: 
				/*Some other protocol like ARP etc*/
				fprintf(stderr, "\nUnhandled frame protocol %#x", ntohs(ethdr->h_proto));
				break;
	}
}

void ProcessIPPacket(struct ethhdr *ethdr, unsigned char* buffer, int size)
{
	//Get the IP Header part of this packet
	struct iphdr *iph = (struct iphdr*)(buffer);
	++total;
	switch (iph->protocol) //Check the Protocol and do accordingly...
	{
		case IPPROTO_ICMP:  
			++icmp;
			print_icmp_packet(ethdr, buffer, size);
			break;

		case IPPROTO_IGMP: 
			++igmp;
			break;

		case IPPROTO_TCP: 
			++tcp;
			print_tcp_packet(ethdr, buffer, size);
			break;

		case IPPROTO_UDP: 
			++udp;
			print_udp_packet(ethdr, buffer, size);
			break;

		case IPPROTO_IPV6:
			abort();
			break;

		default: //Some Other Protocol 
			++others;
			break;
	}
	printf("TCP : %d   UDP : %d   ICMP : %d   IGMP : %d   Others : %d   Total : %d\r",tcp,udp,icmp,igmp,others,total);
}

void print_frame_header(struct ethhdr *ethdr)
{
	fprintf(logfile, "\nFrame Header\n"); 
	fprintf(logfile, "Dst Mac Addr : %2x.%2x.%2x.%2x.%2x.%2x\n", ethdr->h_dest[0], ethdr->h_dest[1], ethdr->h_dest[2], ethdr->h_dest[3], ethdr->h_dest[4], ethdr->h_dest[5]); 
	fprintf(logfile, "Src Mac Addr : %2x.%2x.%2x.%2x.%2x.%2x\n", ethdr->h_source[0], ethdr->h_source[1], ethdr->h_source[2], ethdr->h_source[3], ethdr->h_source[4], ethdr->h_source[5]);
	fprintf(logfile, "protocol     : 0x%2x\n", ntohs(ethdr->h_proto));
}
void print_ip_header(struct ethhdr *ethdr,  unsigned char* Buffer, int Size)
{
	unsigned short iphdrlen;

	struct iphdr *iph = (struct iphdr *)(Buffer);
	iphdrlen = iph->ihl*4;

	print_frame_header(ethdr);

	memset(&source, 0, sizeof(source));
	source.sin_addr.s_addr = iph->saddr;

	memset(&dest, 0, sizeof(dest));
	dest.sin_addr.s_addr = iph->daddr;

	fprintf(logfile,"\n");
	fprintf(logfile,"IP Header\n");
	fprintf(logfile,"   |-IP Version           : %d\n",(unsigned int)iph->version);
	fprintf(logfile,"   |-IP Header Length     : %d DWORDS or %d Bytes\n",(unsigned int)iph->ihl,((unsigned int)(iph->ihl))*4);
	fprintf(logfile,"   |-Type Of Service      : %d\n",(unsigned int)iph->tos);
	fprintf(logfile,"   |-IP Total Length      : %d  Bytes(Size of Packet)\n",ntohs(iph->tot_len));
	fprintf(logfile,"   |-Identification       : %#4.4x\n", (ntohs(iph->id)));

	/*TODO: I should confirm that the bitoperations are correct here*/
	fprintf(logfile,"   |-Reserved ZERO Field  : %d\n",(iph->frag_off & 0x8000)>>15);
	fprintf(logfile,"   |-Dont Fragment Field  : %d\n",(iph->frag_off & 0x4000)>>14);
	fprintf(logfile,"   |-More Fragment Field  : %d\n",(iph->frag_off & 0x2000)>>13);
	fprintf(logfile,"   |-Fragment offset      : %#x\n", ntohs(iph->frag_off & 0x1FFF));
	fprintf(logfile,"   |-TTL                  : %d\n",(unsigned int)iph->ttl);
	fprintf(logfile,"   |-Protocol             : %d\n",(unsigned int)iph->protocol);
	fprintf(logfile,"   |-Checksum             : %d\n",ntohs(iph->check));
	fprintf(logfile,"   |-Source IP            : %s\n",inet_ntoa(source.sin_addr));
	fprintf(logfile,"   |-Destination IP       : %s\n",inet_ntoa(dest.sin_addr));
}

void print_tcp_packet(struct ethhdr *ethdr,  unsigned char* Buffer, int Size)
{
	unsigned short iphdrlen;

	struct iphdr *iph = (struct iphdr *)(Buffer);
	iphdrlen = iph->ihl*4;

	struct tcphdr *tcph=(struct tcphdr*)(Buffer + iphdrlen );

	fprintf(logfile,"\n\n***********************TCP Packet*************************\n");

	print_ip_header(ethdr, Buffer,Size);

	fprintf(logfile,"\n");
	fprintf(logfile,"TCP Header\n");
	fprintf(logfile,"   |-Source Port          : %u\n",	 ntohs(tcph->source));
	fprintf(logfile,"   |-Destination Port     : %u\n",	 ntohs(tcph->dest));
	fprintf(logfile,"   |-Sequence Number      : %#x\n", ntohl(tcph->seq));
	fprintf(logfile,"   |-Acknowledge Number   : %#x\n", ntohl(tcph->ack_seq));
	fprintf(logfile,"   |-Header Length        : %d DWORDS or %d BYTES\n" ,(unsigned int)tcph->doff,(unsigned int)tcph->doff*4);
	fprintf(logfile,"   |-Urgent Flag          : %d\n",  (unsigned int)tcph->urg);
	fprintf(logfile,"   |-Acknowledgement Flag : %d\n",	 (unsigned int)tcph->ack);
	fprintf(logfile,"   |-Push Flag            : %d\n",  (unsigned int)tcph->psh);
	fprintf(logfile,"   |-Reset Flag           : %d\n",  (unsigned int)tcph->rst);
	fprintf(logfile,"   |-Synchronise Flag     : %d\n",  (unsigned int)tcph->syn);
	fprintf(logfile,"   |-Finish Flag          : %d\n",  (unsigned int)tcph->fin);
	fprintf(logfile,"   |-Window               : %d\n",  ntohs(tcph->window));
	fprintf(logfile,"   |-Checksum             : %d\n",  ntohs(tcph->check));
	fprintf(logfile,"   |-Urgent Pointer       : %d\n",  tcph->urg_ptr);

	fprintf(logfile,"\n");
	fprintf(logfile,"                        DATA Dump                         ");
	fprintf(logfile,"\n");

	fprintf(logfile,"IP Header, (%d)\n", iphdrlen);
	PrintData(Buffer,iphdrlen);

	fprintf(logfile,"TCP Header (%d)\n", tcph->doff*4);
	PrintData(Buffer + iphdrlen,tcph->doff*4);

	fprintf(logfile,"Data Payload (%d)\n", (Size - tcph->doff*4-iph->ihl*4));
	PrintData(Buffer + iphdrlen + tcph->doff*4, (Size - tcph->doff*4-iph->ihl*4));

	fprintf(logfile,"\n###########################################################");
}

void print_udp_packet(struct ethhdr *ethdr,  unsigned char *Buffer , int Size)
{

	unsigned short iphdrlen;
	struct iphdr *iph = (struct iphdr *)(Buffer);
	iphdrlen = iph->ihl*4;

	struct udphdr *udph = (struct udphdr*)(Buffer + iphdrlen);

	fprintf(logfile,"\n\n***********************UDP Packet*************************\n");

	print_ip_header(ethdr, Buffer,Size);

	fprintf(logfile,"\nUDP Header\n");
	fprintf(logfile,"   |-Source Port          : %d\n" , ntohs(udph->source));
	fprintf(logfile,"   |-Destination Port     : %d\n" , ntohs(udph->dest));
	fprintf(logfile,"   |-UDP Length           : %d\n" , ntohs(udph->len));
	fprintf(logfile,"   |-UDP Checksum         : %d\n" , ntohs(udph->check));

	fprintf(logfile,"\n");
	fprintf(logfile,"IP Header (%d)\n", iphdrlen);
	PrintData(Buffer , iphdrlen);

	fprintf(logfile,"UDP Header (%d)\n", sizeof(struct udphdr));
	PrintData(Buffer + iphdrlen, sizeof(struct udphdr));

	fprintf(logfile,"Data Payload (%d)\n", (Size - sizeof(struct udphdr) - iph->ihl * 4));
	PrintData(Buffer + iphdrlen + sizeof(struct udphdr), (Size - sizeof(struct udphdr) - iph->ihl * 4));

	fprintf(logfile,"\n###########################################################");
}

void print_icmp_packet(struct ethhdr *ethdr,  unsigned char* Buffer , int Size)
{
	unsigned short iphdrlen;

	struct iphdr *iph = (struct iphdr *)(Buffer);
	iphdrlen = iph->ihl*4;

	struct icmphdr *icmph = (struct icmphdr *)(Buffer + iphdrlen);

	fprintf(logfile,"\n\n***********************ICMP Packet*************************\n");

	print_ip_header(ethdr, Buffer , Size);

	fprintf(logfile,"\n");

	fprintf(logfile,"ICMP Header\n");
	fprintf(logfile,"   |-Type : %d",(unsigned int)(icmph->type));

	if((unsigned int)(icmph->type) == ICMP_TIME_EXCEEDED)
	{
		fprintf(logfile,"  (TTL Expired)\n");
	}
	else if((unsigned int)(icmph->type) == ICMP_ECHOREPLY)
	{
		fprintf(logfile,"  (ICMP Echo Reply)\n");
	}
	fprintf(logfile,"   |-Code : %d\n",(unsigned int)(icmph->code));
	fprintf(logfile,"   |-Checksum : %d\n",ntohs(icmph->checksum));
	fprintf(logfile,"\n");

	fprintf(logfile,"IP Header (%d)\n", iphdrlen);
	PrintData(Buffer, iphdrlen);

	fprintf(logfile,"UDP Header (%d)\n", sizeof(struct icmphdr));
	PrintData(Buffer + iphdrlen , sizeof(struct icmphdr));

	fprintf(logfile,"Data Payload (%d)\n", (Size - (sizeof(struct icmphdr) + iph->ihl * 4)));
	PrintData(Buffer + iphdrlen + sizeof(struct icmphdr) , (Size - (sizeof(struct icmphdr) + iph->ihl * 4)));

	fprintf(logfile,"\n###########################################################");
}

void PrintData (unsigned char* data , int Size)
{

	for(i=0 ; i < Size ; i++)
	{
		if( i!=0 && i%16==0)   //if one line of hex printing is complete...
		{
			fprintf(logfile,"         ");
			for(j=i-16 ; j<i ; j++)
			{
				if(data[j]>=32 && data[j]<=128)
					fprintf(logfile,"%c",(unsigned char)data[j]); //if its a number or alphabet

				else fprintf(logfile,"."); //otherwise print a dot
			}
			fprintf(logfile,"\n");
		}

		if(i%16==0) fprintf(logfile,"   ");
		fprintf(logfile," %02X",(unsigned int)data[i]);

		if( i==Size-1)  //print the last spaces
		{
			for(j=0;j<15-i%16;j++) fprintf(logfile,"   "); //extra spaces

			fprintf(logfile,"         ");

			for(j=i-i%16 ; j<=i ; j++)
			{
				if(data[j]>=32 && data[j]<=128) fprintf(logfile,"%c",(unsigned char)data[j]);
				else fprintf(logfile,".");
			}
			fprintf(logfile,"\n");
		}
	}
}
